﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Argos.Data.Model;
using System.Workflow.Runtime;
using Argos.Workflows;
using System.Threading;
using Argos.WorkflowServices;
using System.Configuration;

namespace Argos.Windows.Forms
{
	/// <summary>
	/// An editor dialog that you use to edit targets with.
	/// </summary>
	public partial class TargetEditorDialog: Form
	{

		#region #ctor: public TargetEditorDialog()

		/// <summary>
		/// Creates a new instance of the class.
		/// </summary>
		public TargetEditorDialog()
		{
			InitializeComponent();
			this.Size = Settings.Default.FormSize;
		}

		#endregion

		#region #ctor: public TargetEditorDialog(WorkflowRuntime runtime)

		/// <summary>
		/// Creates a new instance of the class and specifies the value for
		/// the <see cref="WorkflowRuntime"/> property.
		/// </summary>
		/// <param name="runtime">The value for the <see cref="WorkflowRuntime"/> property.</param>
		public TargetEditorDialog(WorkflowRuntime runtime)
			: this()
		{
			this.WorkflowRuntime = runtime;
		}

		#endregion



		#region public Target SelectedTarget { get; set; }

		/// <summary>
		/// Sets or returns the target being edited in the editor dialog.
		/// </summary>
		/// <exception cref="ArgumentNullException">The exception that is thrown if the property is set to null.</exception>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public Target SelectedTarget
		{
			get { return this.Editor.SelectedObject as Target; }
			set
			{
				if (null == value)
				{
					throw new ArgumentNullException("SelectedTarget");
				}
				this.Editor.SelectedObject = value;
				this.EnableTestLink();
			}
		}

		#endregion
		
		#region public WorkflowRuntime WorkflowRuntime { get; set; }

		private WorkflowRuntime _WorkflowRuntime;

		/// <summary>
		/// Sets or reurns the workflow runtime that is used to test run
		/// the test specified for the target.
		/// </summary>
		public WorkflowRuntime WorkflowRuntime
		{
			get { return _WorkflowRuntime; }
			set
			{
				_WorkflowRuntime = value;
				this.EnableTestLink();
			}
		}

		#endregion



		protected override void OnClosing(CancelEventArgs e)
		{
			base.OnClosing(e);

			if (!e.Cancel && this.WindowState == FormWindowState.Normal)
			{
				Settings.Default.FormSize = this.Size;
				Settings.Default.Save();
			}
		}



		/// <summary>
		/// Enables or disables the <see cref="RunTestLink"/> link button.
		/// </summary>
		/// <remarks>
		/// In order for the link button to be enabled, the <see cref="WorkflowRuntime"/>
		/// property must be set and the <see cref="Target"/> specified  in
		/// <see cref="SelectedTarget"/> must have a test defined.
		/// </remarks>
		private void EnableTestLink()
		{
			this.RunTestLink.Enabled = (null != this.WorkflowRuntime && null != this.SelectedTarget && null != this.SelectedTarget.Test.ActivityType);
		}

		private void RunTestLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{

			// This looks really horrible, but it seems that the test must be executed on
			// another thread than the UI thread.


			this.Cursor = Cursors.WaitCursor;
			this.Enabled = false;

			// This delegate is needed to set the cursor back to the default cursor.
			Action<Cursor> setFormCursor = delegate(Cursor c)
			{
				this.Cursor = c;
			};

			Action<bool> setFormEnabled = delegate(bool enabled)
			{
				this.Enabled = enabled;
			};

			// Shows the OK message if the test passes.
			Action showOKDialog = delegate()
			{
				MessageBox.Show(this, "Test executed successfully.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
			};

			// Shows the exeption thrown by the test execution service.
			Action<Exception> showExceptionDialog = delegate(Exception ex)
			{
				MessageBox.Show(this, string.Format("An exception occured while executing the test.\n\n{0}", ex), "Test Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
			};

			// The delegate that is executed on a thread pool thread and that executes
			// the test using the test execution service.
			WaitCallback executor = delegate(object state)
			{
				try
				{
					ITestExecutionService svc = this.WorkflowRuntime.GetService<ITestExecutionService>();
					svc.RunTest(this.SelectedTarget.Test);
					this.Invoke(showOKDialog);
				}
				catch (Exception ex)
				{
					this.Invoke(showExceptionDialog, ex);
				}
				finally
				{
					this.Invoke(setFormEnabled, true);
					this.Invoke(setFormCursor, Cursors.Default);
				}
			};

			// Queue the test execution to be executed by a thread from the thread pool.
			ThreadPool.QueueUserWorkItem(executor);
		}

		private void Editor_PropertyValueChanged(object sender, PropertyValueChangedEventArgs e)
		{
			this.EnableTestLink();
		}

		private void OK_Click(object sender, EventArgs e)
		{
			if (string.IsNullOrEmpty(this.SelectedTarget.Name))
			{
				MessageBox.Show(this, "The target must have a name. Please give the target a name.", "Name Missing", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return;
			}

			if (null == this.SelectedTarget.MonitoringWorkflowType)
			{
				MessageBox.Show(this, "The monitoring type has not been specified. Please select a monitoring type.", "Monitoring Type Missing", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return;
			}

			if (null == this.SelectedTarget.Test.ActivityType)
			{
				MessageBox.Show(this, "The test activity has not been specified. Please select a test activity.", "Test Activity Missing", MessageBoxButtons.OK, MessageBoxIcon.Warning);
				return;
			}


			this.DialogResult = DialogResult.OK;
			this.Close();
		}



		private class Settings : ApplicationSettingsBase
		{

			public static Settings Default = (Settings)ApplicationSettingsBase.Synchronized(new Settings());

			[UserScopedSetting]
			[DefaultSettingValue("360, 300")]
			public Size FormSize
			{
				get { return (Size)this["FormSize"]; }
				set { this["FormSize"] = value; }
			}

		}


	}
}
